#!/usr/bin/perl

use strict;
use warnings;

use Finance::TA;
use DBI;
use Data::Dumper;
use Date::Manip;
use lib "Modules/";
use MachineFinance;
use FinanceUtils;
use Image::Magick;

my $DBCon = DBI->connect("DBI:mysql:database=PFinance;host=localhost", "PFinance", "");

#my @machines = qw(StochSineMachine MESASineMachine MESAStochSineMachine);
my @machines = qw(TestMachine);

my @orders;
open (my $stocks, "<", "100_list") or die "Failed to open stock file";
while (<$stocks>){
    chomp;
    my $stock = $_;
    for my $machine (@machines){
	my %stats = ( profit => 0, trades => 0, lastOpen => 0, lastPos => 0 );
	my %prices = ( close => getPrices($stock, "close"),
		       open  => getPrices($stock, "open"),
		       high => getPrices($stock, "high"),
		       low  => getPrices($stock, "low"),
		       date => getPrices($stock, "date"));
        $prices{name}=$stock;
	my $dl = $#{$prices{close}};
        print "$dl points for $stock\n";
        next unless @{$prices{close}};
	my $indicators = getInds($machine, $dl, \%prices);
	my $state = { id => "unknown", changed => 0 };
        $stats{position}=[];
	for my $i ( 60 .. $#{$prices{close}} ){
            processOrderClose(\@orders, \%prices, $i);
            processOrderOpens(\@orders, \%prices, $i);
	    $state = getState($machine, $state->{id}, $i, \%prices, $indicators);
            if($state->{changed}){
                #print Dumper \@orders;
                for (@orders){
                    die "Undefined status" unless defined $_->{status};
                    $state->{order}=0 if $_->{sym} eq $prices{name} and $_->{status} eq "pending";
                }
                if($state->{order}){
                    my %newOrder=( sym => $prices{name}, type => $state->{type}, status => "pending" );
                    $newOrder{entryPrice} = $state->{entryPrice} if exists $state->{entryPrice};
                    $newOrder{slPrice} = $state->{slPrice} if exists $state->{slPrice};
                    $newOrder{targetPrice} = $state->{targetPrice} if exists $state->{targetPrice};
                    $newOrder{stopPrice} = $state->{stopPrice} if exists $state->{stopPrice};
                    push @orders, \%newOrder;
                    $state->{order} = 0;
                }
                if($state->{cancelPending}){
                    for my $i ( 0 .. $#orders ){
                        if($orders[$i]->{status} eq "pending" and $orders[$i]->{sym} eq $prices{name}){
                            print "Deleted pending $orders[$i]->{type} order at $orders[$i]->{slPrice}\n";
                            delete $orders[$i];
                            #print Dumper \@orders;
                        }
                    }
                    $state->{cancelPending} = 0;
                }
                $state->{changed} = 0;
            }
	}
        unshift $indicators->{lowLevel}, undef while $#{$indicators->{lowLevel}} < $dl;
        unshift $indicators->{highLevel}, undef while $#{$indicators->{highLevel}} < $dl;
        generatePriceImage( \%prices, "OHLC-$stock-$machine", $indicators->{typical}, $indicators->{highLevel}, $indicators->{lowLevel} );
        #my $images = Image::Magick->New();
	#generateLineImage( "LRSlope-$stock-$machine", $prices{date}, $indicators->{lrslopeh}, $indicators->{lrslopel} );
        #$images->Read("OHLC-$stock-$machine.png");
        #$images->Read("LRSlope-$stock-$machine.png");
        #$images->Read("Stochastics for $stock using $machine.png");
        #$images->Read("UO for $stock using $machine.png");
        #$images->Read("Sine for $stock using $machine.png");
        #$images->Append(stack=>"true")->Write("$stock stacked image for $machine.jpg");
    }
}

no warnings;
my $message="<HTML><BODY><TABLE>";
my $pendingCount;
for my $machine (@machines){
    printf "%s\n", "Report for $machine";
    printf "%-6s | %10s | %20s | %20s | %8s\n", "Stock", "Profit", "Winners", "Losers";
    seek $stocks, 0, 0;
    while (<$stocks>){
        chomp;
        my $stock = $_;
	my $stats = { profit => 0, winners => 0, losers => 0 };
        for my $order (@orders){
            $stats->{profit}+=$order->{profit} if $order->{status} eq "closed" and $order->{sym} eq $stock;
            $stats->{winners}++ if $order->{status} eq "closed" and $order->{sym} eq $stock and $order->{profit}>0;
            $stats->{losers}++ if $order->{status} eq "closed" and $order->{sym} eq $stock and $order->{profit}<0;
        }
        printf "%-6s | %10s | %20s | %20s | %8s\n", "$stock", "$stats->{profit}", "$stats->{winners}", $stats->{losers};
    }
    close $stocks;
    for my $order (@orders){
        next if $order->{state} ne "pending";
        $pendingCount++;
        $message=$message."<TR><TD>Pending order for $order->{sym} at $order->{stopPrice}</TD></TR>\n";
    }
}
$message=$message."</BODY></HTML>\n";
open(my $mail, "|/usr/sbin/sendmail -t") or die "Can't open sendmail, $!\n";

print $mail "To: cbarmstrong\@gmail.com\n";
print $mail "From: cbarmstrong\@gmail.com\n";
print $mail "Subject: Finance Report\n";
print $mail "Content-type: text/html\n\n";
print $mail $message;
close $mail;


sub getPrices {
    my ( $stock, $price ) = @_;
    my $sth = $DBCon->prepare("select $price from prices where stock = '$stock'");
    $sth->execute();
    my @priceArray = ();
    while(my $row = $sth->fetchrow_arrayref){
        push @priceArray, $row->[0];
    }
    return \@priceArray;
}

sub processOrderOpens {
    my $orders = shift;
    my $prices = shift;
    my $index = shift;
    for my $order (@$orders){
        next if $order->{sym} ne $prices->{name};
        next if $order->{status} ne "pending";
        if ( $order->{type} =~ /AtNextOpen/ ){
            $order->{entryPrice} = $prices->{open}->[$index+1];
            $order->{entryDate} = $prices->{date}->[$index+1];
            $order->{status} = "open";
        }
        if ( $order->{type} eq "sellAtStop" ){
            if($prices->{low}->[$index] < $order->{stopPrice}){
                $order->{entryPrice} = $order->{stopPrice};
                $order->{entryDate} = $prices->{date}->[$index];
                $order->{status} = "open";
                $order->{position} = -1;
            }
        }
        if ( $order->{type} eq "buyAtStop" ){
            if($prices->{high}->[$index] > $order->{stopPrice}){
                $order->{entryPrice} = $order->{stopPrice};
                $order->{entryDate} = $prices->{date}->[$index];
                $order->{status} = "open";
                $order->{position} = 1;
            }
        }
        print "$order->{position} order opened for $order->{sym} on $order->{entryDate} at $order->{entryPrice}\n" 
               if $order->{status} eq "open" and $order->{entryDate} eq $prices->{date}->[$index];
    }
}

sub processOrderClose {
    my $orders = shift;
    my $prices = shift;
    my $index = shift;
    for my $order (@$orders){
        next if $order->{sym} ne $prices->{name};
        next if $order->{status} eq "closed";
        if( defined $order->{slPrice} and ( ( $order->{position}>0 and $prices->{low}->[$index]<$order->{slPrice} ) or ( $order->{position}<0 and $prices->{high}->[$index]>$order->{slPrice} ))){
            $order->{exitPrice} = $order->{slPrice};
            $order->{exitDate} = $prices->{date}->[$index];
            $order->{status}="closed";
            $order->{profit}=($order->{exitPrice}-$order->{entryPrice}) * $order->{position};
        } elsif( defined $order->{targetPrice} and ( 
                             ( $order->{position}>0 and $prices->{high}->[$index]>$order->{targetPrice} ) or 
                             ( $order->{position}<0 and $prices->{low}->[$index]<$order->{targetPrice} ))){
            $order->{exitPrice} = $order->{targetPrice};
            $order->{exitDate} = $prices->{date}->[$index];
            $order->{status}="closed";
            $order->{profit}=($order->{exitPrice}-$order->{entryPrice}) * $order->{position};
        }
        print "Order closed for $order->{sym} on $order->{exitDate} at $order->{exitPrice} - $order->{profit}\n" 
               if $order->{status} eq "closed" and $order->{exitDate} eq $prices->{date}->[$index];
    }
}

